package com.sinodata.bsm.center.bean;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.quartz.CronExpression;

import com.sinodata.bsm.center.cache.DefaultEventExcludeRuleCache;
import com.sinodata.bsm.center.cache.DefaultEventRuleCache;
import com.sinodata.bsm.center.cache.EventRuleCache;
import com.sinodata.bsm.center.cache.EventTypeCache;
import com.sinodata.bsm.center.engine.event.EventEngine;
import com.sinodata.bsm.center.expression.Expressions;
import com.sinodata.bsm.center.util.spring.SpringContextHolder;
import com.sinodata.bsm.common.constants.EventConstants;
import com.sinodata.bsm.common.utils.StringUtil;
import com.sinodata.bsm.common.vo.DefaultEventRule;
import com.sinodata.bsm.common.vo.Event;
import com.sinodata.bsm.common.vo.EventRule;
import com.sinodata.bsm.common.vo.EventType;
import com.sinodata.bsm.common.vo.Res;

/**
 * 
 * <p>
 * Description: 事件规则Bean
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-7-10 PM 3:29:11     liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see
 */
public class EventRuleBean {

    private static EventRuleCache eventRuleCache = SpringContextHolder.getBean(EventRuleCache.class);

    private static EventTypeCache eventTypeCache = SpringContextHolder.getBean(EventTypeCache.class);

    private static DefaultEventRuleCache defaultEventRuleCache = SpringContextHolder.getBean(DefaultEventRuleCache.class);

    private static DefaultEventExcludeRuleCache defaultEventExcludeRuleCache = SpringContextHolder.getBean(DefaultEventExcludeRuleCache.class);

    private static com.sinodata.bsm.center.service.resource.ResService resService = SpringContextHolder.getBean(com.sinodata.bsm.center.service.resource.ResService.class);

    private static final Logger logger = Logger.getLogger(EventRuleBean.class);

    private Long last_match_time = 0L;

    private Boolean status = false;

    private EventRule eventRule;

    protected EventRuleBean() {

    }

    /**
     * 传入eventVO对象的构造方法,只允许EventRuleCache调用
     * 
     * @param eventRule
     */
    public EventRuleBean(EventRule eventRule) {
        this.eventRule = eventRule;
    }

    /**
     * 通过eventRule的ID从EventRuleCache中获取Bean对象
     * 
     * @param id
     * @return
     */
    public static EventRuleBean get(Long id) {
        return eventRuleCache.getEventRuleBean(id);
    }

    /**
     * 获取事件类别
     * 
     * @return
     */
    public EventType eventType() {
        return eventTypeCache.get(this.eventRule.getEventTypeId());
    }

    /**
     * 获取事件规则VO对象
     * 
     * @return
     */
    public EventRule eventRule() {
        return eventRule;
    }

    /**
     * 根据资源ID、指标ID获取有效事件规则
     * 
     * @param resId
     * @param propId
     * @return
     */
    public static List<EventRuleBean> getAvailableRules(Long resId, Long propId) {
        List<EventRuleBean> beans = new ArrayList<EventRuleBean>();
        List<EventRule> list = eventRuleCache.getEventRule(resId, propId);
        for (EventRule rule : list) {
            EventRuleBean eventRuleBean = get(rule.getId());
            if (eventRuleBean != null && eventRuleBean.isAvailable()) {
                beans.add(eventRuleBean);
            }
        }
        return beans;
    }

    /**
     * 判断事件规则是否有效，在时间返回内 如果没有设置时间（时间设置小于0），则始终有效; 如果开始时间等于结束时间，则始终有效
     * 如果开始时间小于结束时间，则表示时段在当日内; 如果开始时间大于结束时间，则表示时段跨日。
     * 
     * @return
     */
    public Boolean isAvailable() {
        String vailTime = this.eventRule.getVailTime();
        if (vailTime == null || vailTime.equals("0")) {//全天
            return true;
        } else {//指定时间段
            String[] args = vailTime.split("@");
            long durTime = Long.parseLong(args[1]);
            try {
                CronExpression cron = new CronExpression(args[0]);
                Date d = new Date(System.currentTimeMillis() - durTime * 1000L);
                d = cron.getNextValidTimeAfter(d);
                return d.getTime() < System.currentTimeMillis();
            } catch (ParseException e) {
                logger.error("event valid interval format is wrong ", e);
                return true;
            }
        }
    }

    /**
     * 该规则当前是否匹配,匹配返回true否则返回false 当匹配过程中出错时返回false
     * 
     * @return
     */
    public Boolean matching() {
        Boolean match = false;
        try {
            //            String objectExpr = this.eventRule.getObjectExpr();
            //            if (objectExpr != null && !objectExpr.equals("")) {
            //                String[] objectExprs = objectExpr.split(",");
            //                String keyCol = objectExprs[0];
            //                this.eventRule.setExpr(transExpr(this.eventRule.getExpr(), keyCol));
            //                this.eventRule.setMsg(transExpr(this.eventRule.getMsg(), keyCol));
            //            }
            Object value = Expressions.eval(this.eventRule.getExpr());
            if (!(value instanceof Boolean)) {
                this.status = false;
            } else {
                match = (Boolean) value;
                if (match) {
                    if (this.last_match_time == 0) {
                        this.status = this.eventRule.getDuration() <= 0;
                    } else {
                        this.status = System.currentTimeMillis() - this.last_match_time >= this.eventRule.getDuration() * 1000L;
                    }
                } else {
                    this.status = false;
                }
            }
        } catch (Exception ex) {
            logger.error("match rule error:" + this.eventRule.getExpr(), ex);
            this.status = false;
        } finally {
            if (match && this.last_match_time == 0) {
                this.last_match_time = System.currentTimeMillis();
            } else if (!match && this.last_match_time != 0) {
                this.last_match_time = 0L;
            }
        }
        return this.status;
    }

    /**
     * 产生事件，当事件规则匹配时产生事件,使用该方法之前调用matching()方法
     * 
     * @return
     * @throws Exception
     */
    public Event makeEvent(String objectId) throws Exception {
        Event event = new Event();
        //event.setId(-1L);
        event.setResId(eventRule.getResId());
        event.setEventTypeId(eventRule.getEventTypeId());
        event.setLevel(eventRule.getLevel());
        //objectId 不为空的时候，消息表达式应该被替换，在调用makeEvent时替换
        if (objectId != null) {
            event.setObjectId(objectId);//设置对象ID，例如表格类型事件。
            String msg = eventRule.getMsg();
            msg = msg.replace("$objectId", objectId);//用Key值替换表达式
            eventRule.setMsg(msg);
        }
        Object msg = Expressions.eval(eventRule.getMsg());
        if (msg != null && msg instanceof String) {
            event.setMsg((String) msg);
        }
        event.setStatus(EventConstants.STATUS_NEW);
        event.setOccurTime(new Date(System.currentTimeMillis()));
        event.setCatalog(EventConstants.CATALOG_FAULTEVENT);
        event.setMsgExpr(eventRule.getMsg());
        //添加事件产生次数,最后产生时间
        event.setCount(1);
        event.setLastOccurTime(event.getOccurTime());
        //关联指标,关联指标需要此数据
        event.setRuleExpr(eventRule.getExpr());
        return event;
    }

    /**
     * 添加事件规则，加入事件规则缓存、加入指标关联缓存、进行事件规则注册
     * 
     * @param eventRule
     */
    public static void addEventRule(EventRule eventRule) {
        eventRuleCache.put(eventRule.getId(), eventRule);
        eventRuleCache.linkRuleWithProperty(eventRule);
        EventEngine.registry(EventRuleBean.get(eventRule.getId()));
    }

    /**
     * 移除事件规则，解除事件规则注册、移除与指标关联缓存、移除事件规则缓存
     * 
     * @param eventRule
     */
    public static void removeEventRule(EventRule eventRule) {
        EventEngine.unregistry(EventRuleBean.get(eventRule.getId()));
        eventRuleCache.unlinkRuleWithProperty(eventRule);
        eventRuleCache.remove(eventRule.getId());
    }

    /**
     * 停用事件规则，解除事件规则注册，移除与指标关联缓存,更新缓存
     * 
     * @param eventRule
     */
    public static void stopEventRule(EventRule eventRule) {
        EventEngine.unregistry(EventRuleBean.get(eventRule.getId()));
        eventRuleCache.unlinkRuleWithProperty(eventRule);
        eventRuleCache.put(eventRule.getId(), eventRule);
    }

    /**
     * 启用事件规则，更新事件规则缓存、加入指标关联缓存、进行事件规则注册
     * 
     * @param eventRule
     */
    public static void startEventRule(EventRule eventRule) {
        addEventRule(eventRule);
    }

    /**
     * 全局停用，该默认规则对应注册的资源事件规则全部解除，解除指标注册，更新默认事件规则缓存
     * 
     * @param eventRule
     */
    public static void stopDefaultEventRule(DefaultEventRule defaultEventRule) {
        Long resTypeId = defaultEventRule.getResTypeId();
        //获取该类别的资源及其子类别的资源
        List<Res> resList = resService.findByResTypeId(resTypeId);
        if (resList == null) {
            return;
        }
        for (Res res : resList) {
            Long resId = res.getId();
            //stopDefaultEventRule(defaultEventRule, res.getId());//不调用这个了，复制了一下，更新缓存在最后一步做就OK
            EventEngine.unregistry(defaultEventRule, resId);
            String expr = StringUtil.replace(defaultEventRule.getExpr(), "$resid", resId + "");
            String ruleKey = resId + "&" + defaultEventRule.getId() + "&" + defaultEventRule.getLevel() + "&" + (defaultEventRule.getVailTime() == null ? "" : defaultEventRule.getVailTime());
            eventRuleCache.unlinkRuleWithProperty(expr, ruleKey);
        }
        defaultEventRuleCache.put(defaultEventRule.getId(), defaultEventRule);
    }

    /**
     * 全局启用，注册非排除资源的事件规则，指标注册，更新默认事件规则缓存
     * 
     * @param eventRule
     */
    public static void startDefaultEventRule(DefaultEventRule defaultEventRule) {
        Long resTypeId = defaultEventRule.getResTypeId();
        //获取该类别的资源及其子类别的资源
        List<Res> resList = resService.findByResTypeId(resTypeId);
        if (resList == null) {
            return;
        }
        for (Res res : resList) {
            Long resId = res.getId();
            if (defaultEventExcludeRuleCache.getByResIdDefaultEventRuleId(resId, defaultEventRule.getId()) != null) { // 包含子排除资源
                continue;
            }
            //startDefaultEventRule(defaultEventRule, res.getId());//不调用这个了，复制了一下，更新缓存在最后一步做就OK
            EventRule eventRule = EventEngine.transEventRule(defaultEventRule, resId);
            EventRuleBean eventRuleBean = new EventRuleBean(eventRule);
            eventRuleCache.putEventRuleBean(eventRuleBean);
            eventRuleCache.linkRuleWithProperty(eventRule);
            EventEngine.registry(eventRuleBean);
        }
        defaultEventRuleCache.put(defaultEventRule.getId(), defaultEventRule);
    }

    /**
     * 停用某资源的默认事件规则,根据Key值解除虚拟事件规则，解除指标注册，更新默认事件规则缓存
     * 
     * @param eventRule
     */
    public static void stopDefaultEventRule(DefaultEventRule defaultEventRule, Long resId) {
        EventEngine.unregistry(defaultEventRule, resId);
        String expr = StringUtil.replace(defaultEventRule.getExpr(), "$resid", resId + "");
        String ruleKey = resId + "&" + defaultEventRule.getId() + "&" + defaultEventRule.getLevel() + "&" + (defaultEventRule.getVailTime() == null ? "" : defaultEventRule.getVailTime());
        eventRuleCache.unlinkRuleWithProperty(expr, ruleKey);
        defaultEventRuleCache.put(defaultEventRule.getId(), defaultEventRule);
    }

    /**
     * 启用某资源的默认事件规则，注册事件规则，放入指标关联缓存，更新默认事件规则缓存
     * 
     * @param eventRule
     */
    public static void startDefaultEventRule(DefaultEventRule defaultEventRule, Long resId) {
        //转换后关联指标和注册到引擎
        EventRule eventRule = EventEngine.transEventRule(defaultEventRule, resId);
        EventRuleBean eventRuleBean = new EventRuleBean(eventRule);
        eventRuleCache.putEventRuleBean(eventRuleBean);
        eventRuleCache.linkRuleWithProperty(eventRule);
        EventEngine.registry(eventRuleBean);
        defaultEventRuleCache.put(defaultEventRule.getId(), defaultEventRule);
    }

    /**
     * 获取事件规则定义的级别
     * 
     * @return
     */
    public int getEventLevle() {
        return eventRule.getLevel();
    }

    public boolean getStatus() {
        return this.status;
    }

    /**
     * TODO 把形如tprop(resId,propId,col) 转换成 tprop(resId,propId,keyCol,keyValue,col)
     * 其中keyValue 使用 "$objectId" 占位
     * 
     * @param expr
     * @return
     */
    public static String transExpr(String expr, String keyCol) {
        //Pattern pattern = Pattern.compile("(tprop\\((\\d+),(\\d+[L|l]?),(\\S+)\\)).*");
        Pattern pattern = Pattern.compile("(tprop\\((\\d+),(\\d+[L|l]?),([^\\)]+)\\)(.*))");
        Matcher matcher = pattern.matcher(expr);
        StringBuffer result = new StringBuffer();
        //while (matcher.find()) {
        if (matcher.find()) {
            result.append("tprop(");
            result.append(matcher.group(2));
            result.append(",");
            result.append(matcher.group(3));
            result.append(",");
            result.append("\"");
            result.append(keyCol);
            result.append("\"");
            result.append(",");
            result.append("\"");
            result.append("$objectId");
            result.append("\"");
            result.append(",");
            result.append("\"");
            result.append(matcher.group(4));
            result.append("\"");
            result.append(")");
            //result = tprop(25625,120201110000003L,状态,$objectId,状态)
            //expr = StringUtil.replace(expr, matcher.group(1), result.toString());
            result.append(matcher.group(5));
        }
        //return expr;
        return result.toString();
    }
}